package com.example.backend.exception;

import com.example.backend.enums.ResultCodeEnum;
import com.example.backend.utils.ResultJson;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@RestControllerAdvice
@Log4j2
public class GlobalExceptionHandler {

    /**
     * 处理参数校验异常 (JSR-303)
     */

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultJson<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("参数校验异常: {}", e.getMessage(), e);

        // 收集所有字段的错误信息
        List<String> errorMessages = e.getBindingResult().getAllErrors().stream()
                .map(objectError -> {
                    if (objectError instanceof FieldError) {
                        FieldError fieldError = (FieldError) objectError;
                        return fieldError.getField() + ": " + fieldError.getDefaultMessage();
                    }
                    return objectError.getDefaultMessage();
                })
                .collect(Collectors.toList());

        // 返回带有错误列表的响应
        return ResultJson.fail(ResultCodeEnum.PARAM_ERROR.getCode(), "参数校验失败" + errorMessages);
    }

    /**
     * 处理路径参数校验异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResultJson<?> handleConstraintViolationException(ConstraintViolationException e) {
        log.error("路径参数校验异常: {}", e.getMessage(), e);
        String errorMsg = e.getConstraintViolations().iterator().next().getMessage();
        return ResultJson.error(ResultCodeEnum.PARAM_ERROR.getCode(), errorMsg);
    }

    /**
     * 处理参数类型不匹配异常
     */

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResultJson<?> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        log.error("参数类型不匹配异常: {}", e.getMessage(), e);
        return ResultJson.error(ResultCodeEnum.PARAM_ERROR.getCode(),
                "参数类型错误，需要 " + Objects.requireNonNull(e.getRequiredType()).getSimpleName());
    }

    /**
     * 处理请求体解析异常
     */

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResultJson<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error("请求体解析异常: {}", e.getMessage(), e);
        return ResultJson.error(ResultCodeEnum.PARAM_ERROR.getCode(), "请求体格式错误" + e.getMessage());
    }

    /**
     * 处理缺少请求参数异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResultJson<?> handleMissingParamException(MissingServletRequestParameterException e) {
        log.error("缺少请求参数: {}", e.getMessage());
        return ResultJson.error(400, "缺少必需参数: " + e.getParameterName());
    }

    /**
     * 处理其他未捕获的异常
     */
    @ExceptionHandler(Exception.class)
    public ResultJson<?> handleException(Exception e) {
        log.error("系统异常: {}", e.getMessage(), e);
        return ResultJson.error(ResultCodeEnum.INTERNAL_SERVER_ERROR.getCode(), "服务器内部错误，请稍后重试" + e.getMessage());
    }

    @ExceptionHandler(AccessDeniedException.class)
    public ResultJson<?> handleAccessDeniedException(AccessDeniedException e) {
        log.error("权限不足: {}", e.getMessage(), e);
        return ResultJson.error(ResultCodeEnum.FORBIDDEN.getCode(), "权限不足");
    }

    @ExceptionHandler(InsufficientAuthenticationException.class)
    public ResultJson<?> handleAuthenticationEntryPoint(InsufficientAuthenticationException e) {
        log.error("未登录错误: {}", e.getMessage(), e);
        return ResultJson.error(ResultCodeEnum.UNAUTHORIZED.getCode(), "请先登录");
    }

    /**
     * Jwt过期错误
     */
    @ExceptionHandler(JwtExpiredException.class)
    public ResultJson<?> handleJwtExpiredException(JwtExpiredException e) {
        log.error("Jwt过期错误: {}", e.getMessage(), e);
        return ResultJson.error(ResultCodeEnum.TOKEN_EXPIRED.getCode(), e.getMessage());
    }
}

